package pl.rzarajczyk.breaktime;

import pl.rzarajczyk.breaktime.utils.Utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import javax.swing.JFrame;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 *
 * @author rzarajczyk
 */
public class Localization {

    @Autowired
    private BreakTimeSettings settings;

    private File dir;
    private List<Locale> available;

    private Locale locale;
    private Properties texts;

    private Properties collect;

    private Log log = LogFactory.getLog(getClass());

    public static String toString(Locale locale) {
        return locale.getLanguage() + "_" + locale.getCountry();
    }

    public static Locale fromString(String string) {
        if ( string == null )
            return null;
        String language = string;
        String country = string;
        int underscore = string.indexOf('_');
        if ( underscore >= 0 ) {
            country = language.substring(underscore + 1);
            language = language.substring(0, underscore);
        }
        return new Locale(language, country);
    }

    public void open() throws IOException {
        dir = new File(Utils.getSettingsDir(), "languages");
        if ( ! dir.exists() ) {
            Utils.unpackFromClasspathDir("languages", Utils.getSettingsDir());
        }

        this.available = new ArrayList<Locale>();
        for ( File file : dir.listFiles( new PropertiesFilenameFilter() ) ) {
            String name = file.getName();
            name = name.substring(0, name.length() - BreakTimeSettings.LOCALIZATION_EXT.length());
            available.add( fromString(name) );
        }

        texts = new Properties();

        Locale localeToLoad = settings.getLocale();
        setLocale(localeToLoad);
    }

    public void setLocale(Locale locale) throws IOException {
        if ( locale != null ) {
            if ( ! getAvailableLocales().contains(locale) )
                throw new IllegalArgumentException("Unsupported locale: " + locale);
            this.locale = locale;
            texts.clear();
            InputStream is = new FileInputStream( new File(dir, toString(locale) + BreakTimeSettings.LOCALIZATION_EXT) );
            try {
                texts.load(is);
            } finally {
                is.close();
            }
            settings.setLocale(locale);
            settings.triggerUpdate();
        }
        if ( BreakTimeSettings.LOCALIZATION_COLLECT_TEXTS )
            collect = new Properties();
    }   

    public Locale getLocale() {
        return locale;
    }

    public List<Locale> getAvailableLocales() {
        return available;
    }

    public String text(Object object, String key, String defaultValue) {
        key = object.getClass().getSimpleName() + "/" + key;
        if ( BreakTimeSettings.LOCALIZATION_COLLECT_TEXTS ) {
            collect.setProperty(key, defaultValue);
            try {
                FileOutputStream fos = new FileOutputStream(new File(Utils.getTempDir(), "collected-translation" + BreakTimeSettings.LOCALIZATION_EXT));
            try {
                collect.save(fos, "Automatically collected translation file of BreakTime!");
            } finally {
                fos.close();
            }
            } catch (IOException e) {
                Utils.error(null, LogFactory.getLog(getClass()), "Could not collect translation", e);
            }
        }
        return texts.getProperty(key, defaultValue);
    }

    public void localize(JFrame jframe) {
        Class<?> clazz = jframe.getClass();
        for ( Field field : clazz.getDeclaredFields() ) {
            if ( field.getAnnotation(Lang.class) != null ) {
                try {
                    field.setAccessible(true);
                    Object actualObject = field.get(jframe);
                    Class<?> actualObjectClass = actualObject.getClass();
                    try {
                        Method getter = actualObjectClass.getMethod("getText");
                        String defaultValue = (String) getter.invoke(actualObject);

                        Method setter = actualObjectClass.getMethod("setText", String.class);
                        setter.invoke(actualObject, text(jframe, field.getName(), defaultValue));
                    } catch (NoSuchMethodException e) {
                        log.warn("Cannot find getText() or setText() on " + actualObject, e);
                    } catch (IllegalAccessException e) {
                        log.warn("Cannot invoke getText() or setText() on " + actualObject, e);
                    } catch (InvocationTargetException e) {
                        log.warn("Cannot invoke getText() or setText() on " + actualObject, e);
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Could not access field value [" + field.getName() + "] in class [" + clazz + "]", e);
                }
            }
        }
        jframe.pack();
    }

    // =========================================================================

    class PropertiesFilenameFilter implements FilenameFilter {
        @Override
        public boolean accept(File dir, String name) {
            return name.endsWith(BreakTimeSettings.LOCALIZATION_EXT);
        }
    }

}
